#include "stdafx.h"
#include "D3DClass.h"


CD3DClass::CD3DClass(void) : m_pSwapChain(nullptr), m_pDevice(nullptr), m_pDeviceContext(nullptr), m_pRenderTargetView(nullptr), m_pDepthStencilBuffer(nullptr), m_pDepthStencilState(nullptr), m_pDepthStencilView(nullptr), m_pRasterState(nullptr), m_pDepthDisabledStencilState(nullptr),
	m_pAlphaEnableBlendingState(nullptr), m_pAlphaDisableBlendingState(nullptr)
{
}


CD3DClass::~CD3DClass(void)
{
}

bool CD3DClass::Initialize(int nScreenWidth, int nScreenHeight, bool bEnableVSync, HWND hWnd, bool bEnableFullScreen, float fScreenDepth, float fScreenNear)
{
	HRESULT hResult;
	IDXGIFactory* pFactory;
	IDXGIAdapter* pAdapter;
	IDXGIOutput* pAdapterOutput;
	unsigned int uNumberOfModes;
	unsigned int uNumerator;
	unsigned int uDenominator;
	unsigned int uStringLength;
	DXGI_MODE_DESC* pDisplayModeList;
	DXGI_ADAPTER_DESC stAdapterDesc;
	int nError;
	DXGI_SWAP_CHAIN_DESC stSwapChainDesc;
	D3D_FEATURE_LEVEL eFeatureLevel;
	ID3D11Texture2D* pBackBuffer;
	D3D11_TEXTURE2D_DESC stDepthBufferDesc;
	D3D11_DEPTH_STENCIL_DESC stDepthStencilDesc;
	D3D11_DEPTH_STENCIL_VIEW_DESC stDepthStencilViewDesc;
	D3D11_RASTERIZER_DESC stRasterDesc;
	D3D11_VIEWPORT stViewport;
	float fFieldOfView;
	float fScreenAspect;
	D3D11_DEPTH_STENCIL_DESC stDepthDisabledStencilDesc;
	D3D11_BLEND_DESC stBlendStateDesc;

	m_bVSyncEnabled = bEnableVSync;

	hResult = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&pFactory);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create the DXGI Factory");
		return false;
	}

	hResult = pFactory->EnumAdapters(0, &pAdapter);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to enumerate the adapters");
		return false;
	}

	hResult = pAdapter->EnumOutputs(0, &pAdapterOutput);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to enumerate the outputs");
		return false;
	}

	hResult = pAdapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &uNumberOfModes, NULL);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to get the display mode list");
		return false;
	}

	pDisplayModeList = new DXGI_MODE_DESC[uNumberOfModes];
	if(!pDisplayModeList)
	{
		DEBUG_OUTPUT("Failed to initialize the DXGI_MODE_DESC");
		return false;
	}

	hResult = pAdapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &uNumberOfModes, pDisplayModeList);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to get the display mode list (second check)");
		return false;
	}

	for(unsigned int i = 0; i < uNumberOfModes; ++i)
	{
		if(pDisplayModeList[i].Width == (unsigned int)nScreenWidth)
		{
			if(pDisplayModeList[i].Height == (unsigned int)nScreenHeight)
			{
				uNumerator = pDisplayModeList[i].RefreshRate.Numerator;
				uDenominator = pDisplayModeList[i].RefreshRate.Denominator;
			}
		}
	}

	hResult = pAdapter->GetDesc(&stAdapterDesc);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to get the adapter description");
		return false;
	}

	m_nVideoCardMemory = (int)(stAdapterDesc.DedicatedVideoMemory/1024/1024);

	nError = wcstombs_s(&uStringLength, m_cVideoCardDescription, 128, stAdapterDesc.Description, 128);
	if(nError != 0)
	{
		DEBUG_OUTPUT("wcstombs_s return an error");
		return false;
	}

	delete[] pDisplayModeList;
	pDisplayModeList = nullptr;

	pAdapterOutput->Release();
	pAdapterOutput = nullptr;

	pAdapter->Release();
	pAdapter = nullptr;

	pFactory->Release();
	pFactory = nullptr;

	ZeroMemory(&stSwapChainDesc, sizeof(stSwapChainDesc));

	stSwapChainDesc.BufferCount = 1;

	stSwapChainDesc.BufferDesc.Width = nScreenWidth;
	stSwapChainDesc.BufferDesc.Height = nScreenHeight;

	stSwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

	if(m_bVSyncEnabled == true)
	{
		stSwapChainDesc.BufferDesc.RefreshRate.Numerator = uNumerator;
		stSwapChainDesc.BufferDesc.RefreshRate.Denominator = uDenominator;
	}
	else
	{
		stSwapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
		stSwapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
	}

	stSwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;

	stSwapChainDesc.OutputWindow = hWnd;

	stSwapChainDesc.SampleDesc.Count = 1;
	stSwapChainDesc.SampleDesc.Quality = 0;

	if(bEnableFullScreen)
	{
		stSwapChainDesc.Windowed = false;
	}
	else
	{
		stSwapChainDesc.Windowed = true;
	}

	stSwapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	stSwapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

	stSwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

	stSwapChainDesc.Flags = 0;

	eFeatureLevel = D3D_FEATURE_LEVEL_11_0;

	hResult = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, &eFeatureLevel, 1, D3D11_SDK_VERSION, &stSwapChainDesc, &m_pSwapChain, &m_pDevice, NULL, &m_pDeviceContext);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create the Device and Swap Chain");
		return false;
	}

	hResult = m_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to get the back buffer");
		return false;
	}

	hResult = m_pDevice->CreateRenderTargetView(pBackBuffer, NULL, &m_pRenderTargetView);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create the Render Target View");
		return false;
	}

	pBackBuffer->Release();
	pBackBuffer = 0;

	ZeroMemory(&stDepthBufferDesc, sizeof(stDepthBufferDesc));

	stDepthBufferDesc.Width = nScreenWidth;
	stDepthBufferDesc.Height = nScreenHeight;
	stDepthBufferDesc.MipLevels = 1;
	stDepthBufferDesc.ArraySize = 1;
	stDepthBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	stDepthBufferDesc.SampleDesc.Count = 1;
	stDepthBufferDesc.SampleDesc.Quality = 0;
	stDepthBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	stDepthBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	stDepthBufferDesc.CPUAccessFlags = 0;
	stDepthBufferDesc.MiscFlags = 0;

	hResult = m_pDevice->CreateTexture2D(&stDepthBufferDesc, NULL, &m_pDepthStencilBuffer);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create the Texture 2D");
		return false;
	}

	ZeroMemory(&stDepthStencilDesc, sizeof(stDepthStencilDesc));

	stDepthStencilDesc.DepthEnable = true;
	stDepthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	stDepthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
	stDepthStencilDesc.StencilEnable = true;
	stDepthStencilDesc.StencilReadMask = 0xFF;
	stDepthStencilDesc.StencilWriteMask = 0xFF;

	stDepthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	stDepthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
	stDepthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	stDepthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

	stDepthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	stDepthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
	stDepthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	stDepthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

	hResult = m_pDevice->CreateDepthStencilState(&stDepthStencilDesc, &m_pDepthStencilState);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create the Depth Stencil State");
		return false;
	}

	ZeroMemory(&stBlendStateDesc, sizeof(D3D11_BLEND_DESC));
	
	stBlendStateDesc.RenderTarget[0].BlendEnable = TRUE;
	stBlendStateDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
	stBlendStateDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
	stBlendStateDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	stBlendStateDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
	stBlendStateDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
	stBlendStateDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	stBlendStateDesc.RenderTarget[0].RenderTargetWriteMask = 0x0f;
	
	hResult = m_pDevice->CreateBlendState(&stBlendStateDesc, &m_pAlphaEnableBlendingState);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create enable blend state");
		return false;
	}
	
	stBlendStateDesc.RenderTarget[0].BlendEnable = false;
	
	hResult = m_pDevice->CreateBlendState(&stBlendStateDesc, &m_pAlphaDisableBlendingState);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create disable blend state");
		return false;
	}

	m_pDeviceContext->OMSetDepthStencilState(m_pDepthStencilState, 1);

	ZeroMemory(&stDepthStencilViewDesc, sizeof(stDepthStencilViewDesc));

	stDepthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	stDepthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
	stDepthStencilViewDesc.Texture2D.MipSlice = 0;

	hResult = m_pDevice->CreateDepthStencilView(m_pDepthStencilBuffer, &stDepthStencilViewDesc, &m_pDepthStencilView);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create the Depth Stencil View");
		return false;
	}

	m_pDeviceContext->OMSetRenderTargets(1, &m_pRenderTargetView, m_pDepthStencilView);

	stRasterDesc.AntialiasedLineEnable = false;
	stRasterDesc.CullMode = D3D11_CULL_BACK;
	stRasterDesc.DepthBias = 0;
	stRasterDesc.DepthBiasClamp = 0.0f;
	stRasterDesc.DepthClipEnable = true;
	stRasterDesc.FillMode = D3D11_FILL_SOLID;
	stRasterDesc.FrontCounterClockwise = false;
	stRasterDesc.MultisampleEnable = false;
	stRasterDesc.ScissorEnable = false;
	stRasterDesc.SlopeScaledDepthBias = 0.0f;

	hResult = m_pDevice->CreateRasterizerState(&stRasterDesc, &m_pRasterState);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create the Rasterizer State");
		return false;
	}

	m_pDeviceContext->RSSetState(m_pRasterState);

	stViewport.Width = (float)nScreenWidth;
	stViewport.Height = (float)nScreenHeight;
	stViewport.MinDepth = 0.0f;
	stViewport.MaxDepth = 1.0f;
	stViewport.TopLeftX = 0.0f;
	stViewport.TopLeftY = 0.0f;

	m_pDeviceContext->RSSetViewports(1, &stViewport);

	fFieldOfView = (float)D3DX_PI / 4.0f;
	fScreenAspect = (float)nScreenWidth / (float)nScreenHeight;

	D3DXMatrixPerspectiveFovLH(&m_d3dProjectionMatrix, fFieldOfView, fScreenAspect, fScreenNear, fScreenDepth);

	D3DXMatrixIdentity(&m_d3dWorldMatrix);

	D3DXMatrixOrthoLH(&m_d3dOrthographicMatrix, (float)nScreenWidth, (float)nScreenHeight, fScreenNear, fScreenDepth);

	ZeroMemory(&stDepthDisabledStencilDesc, sizeof(stDepthDisabledStencilDesc));

	stDepthDisabledStencilDesc.DepthEnable = false;
	stDepthDisabledStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	stDepthDisabledStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
	stDepthDisabledStencilDesc.StencilEnable = true;
	stDepthDisabledStencilDesc.StencilReadMask = 0xFF;
	stDepthDisabledStencilDesc.StencilWriteMask = 0xFF;
	stDepthDisabledStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	stDepthDisabledStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
	stDepthDisabledStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	stDepthDisabledStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
	stDepthDisabledStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	stDepthDisabledStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
	stDepthDisabledStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	stDepthDisabledStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

	hResult = m_pDevice->CreateDepthStencilState(&stDepthDisabledStencilDesc, &m_pDepthDisabledStencilState);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create the depth stencil state");
		return false;
	}

	//ZeroMemory(&stBlendStateDesc, sizeof(D3D11_BLEND_DESC));
	//
	//stBlendStateDesc.RenderTarget[0].BlendEnable = TRUE;
	//stBlendStateDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
	//stBlendStateDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
	//stBlendStateDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	//stBlendStateDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
	//stBlendStateDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
	//stBlendStateDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	//stBlendStateDesc.RenderTarget[0].RenderTargetWriteMask = 0x0f;
	//
	//hResult = m_pDevice->CreateBlendState(&stBlendStateDesc, &m_pAlphaEnableBlendingState);
	//if(FAILED(hResult))
	//{
	//	DEBUG_OUTPUT("Failed to create enable blend state");
	//	return false;
	//}
	//
	//stBlendStateDesc.RenderTarget[0].BlendEnable = false;
	//
	//hResult = m_pDevice->CreateBlendState(&stBlendStateDesc, &m_pAlphaDisableBlendingState);
	//if(FAILED(hResult))
	//{
	//	DEBUG_OUTPUT("Failed to create disable blend state");
	//	return false;
	//}

	return true;
}

void CD3DClass::ShutDown()
{
	if(m_pSwapChain)
	{
		m_pSwapChain->SetFullscreenState(false, NULL);
	}

	if(m_pAlphaEnableBlendingState != nullptr)
	{
		m_pAlphaEnableBlendingState->Release();
		m_pAlphaEnableBlendingState = nullptr;
	}
	if(m_pAlphaDisableBlendingState != nullptr)
	{
		m_pAlphaDisableBlendingState->Release();
		m_pAlphaDisableBlendingState = nullptr;
	}

	if(m_pDepthDisabledStencilState != nullptr)
	{
		m_pDepthDisabledStencilState->Release();
		m_pDepthDisabledStencilState = nullptr;
	}

	if(m_pRasterState)
	{
		m_pRasterState->Release();
		m_pRasterState = nullptr;
	}

	if(m_pDepthStencilView)
	{
		m_pDepthStencilView->Release();
		m_pDepthStencilView = nullptr;
	}

	if(m_pDepthStencilState)
	{
		m_pDepthStencilState->Release();
		m_pDepthStencilState = nullptr;
	}

	if(m_pDepthStencilBuffer)
	{
		m_pDepthStencilBuffer->Release();
		m_pDepthStencilBuffer = nullptr;
	}

	if(m_pRenderTargetView)
	{
		m_pRenderTargetView->Release();
		m_pRenderTargetView = nullptr;
	}

	if(m_pDeviceContext)
	{
		m_pDeviceContext->Release();
		m_pDeviceContext = nullptr;
	}

	if(m_pDevice)
	{
		m_pDevice->Release();
		m_pDevice = nullptr;
	}

	if(m_pSwapChain)
	{
		m_pSwapChain->Release();
		m_pSwapChain = nullptr;
	}
}

void CD3DClass::TurnOnAlphaBlending()
{
	float fBlendFactor[4];

	fBlendFactor[0] = 0.0f;
	fBlendFactor[1] = 0.0f;
	fBlendFactor[2] = 0.0f;
	fBlendFactor[3] = 0.0f;

	m_pDeviceContext->OMSetBlendState(m_pAlphaEnableBlendingState, fBlendFactor, 0xFFFFFFFF);
}

void CD3DClass::TurnOffAlphaBlending()
{
	float fBlendFactor[4];

	fBlendFactor[0] = 0.0f;
	fBlendFactor[1] = 0.0f;
	fBlendFactor[2] = 0.0f;
	fBlendFactor[3] = 0.0f;

	m_pDeviceContext->OMSetBlendState(m_pAlphaDisableBlendingState, fBlendFactor, 0xFFFFFFFF);
}

void CD3DClass::BeginScene(float fRed, float fGreen, float fBlue, float fAlpha)
{
	float fColor[4];

	fColor[0] = fRed;
	fColor[1] = fGreen;
	fColor[2] = fBlue;
	fColor[3] = fAlpha;

	m_pDeviceContext->ClearRenderTargetView(m_pRenderTargetView, fColor);

	m_pDeviceContext->ClearDepthStencilView(m_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);

}

void CD3DClass::EndScene()
{
	if(m_bVSyncEnabled == true)
	{
		m_pSwapChain->Present(1, 0);
	}
	else
	{
		m_pSwapChain->Present(0, 0);
	}
}

ID3D11Device* CD3DClass::GetDevice()
{
	return m_pDevice;
}

ID3D11DeviceContext* CD3DClass::GetDeviceContext()
{
	return m_pDeviceContext;
}

void CD3DClass::GetProjectionMatrix(D3DXMATRIX& d3dProjectionMatrix)
{
	d3dProjectionMatrix = m_d3dProjectionMatrix;
}

void CD3DClass::GetWorldMatrix(D3DMATRIX& d3dWorldMatrix)
{
	d3dWorldMatrix = m_d3dWorldMatrix;
}

void CD3DClass::GetOrthographicMatrix(D3DMATRIX& d3dOrthographicMatrix)
{
	d3dOrthographicMatrix = m_d3dOrthographicMatrix;
}

void CD3DClass::GetVideoCardInfo(char* cCardName, int& nMemory)
{
	strcpy_s(cCardName, 128, m_cVideoCardDescription);
	nMemory = m_nVideoCardMemory;
}

void CD3DClass::TurnZBufferOn()
{
	m_pDeviceContext->OMSetDepthStencilState(m_pDepthStencilState, 1);
}

void CD3DClass::TurnZBufferOff()
{
	m_pDeviceContext->OMSetDepthStencilState(m_pDepthDisabledStencilState, 1);
}